Harden session persistence, worker recovery, and daemon refreshes - #2028
Merged
Conversation
…log substrate The recorder's private append/replay/repair IO is deleted; EventLog owns it, the same move #1987 made for the RLM spawn ledger. One durability rule is unified in the substrate rather than dropped: an unterminated final line is an uncommitted append, skipped on read and truncated before the next append — never newline-completed and never surfaced to a consumer whose next append destroys it.
…atomic readSemanticEdgeLedger probed with statSync before reading through EventLog, which swallows ENOENT; a ledger deleted between the two returned [] instead of throwing. The missing-file decision now lives at the single open (replaySync missingFileThrows), so no check-then-read window exists.
The unterminated-tail contract was restated four times (module doc, replaySync doc, two test comments). It now lives once in the module doc; the method doc keeps only its own parse/missing-file semantics and the test comments reference the contract.
…es-ledger-on-event-log
…tail repair writeSync may write short (ENOSPC after a prefix); appendSync now loops until the payload is fully on disk so write-before-action callers never act on a torn record reported as success. A tail-repair failure (e.g. append-only ACL permitting O_APPEND but not r+) now propagates instead of being swallowed: writing through an unrepaired torn tail would weld it to the new record as permanent interior corruption. ENOENT and the concurrent-writer instability path keep their existing semantics.
…ng them The rlm spawn ledger is multi-writer by documented design (supervisor plus each worker over one file), so completing a short O_APPEND write with a second write could interleave with a rival append and weld two records. A short write now truncates its own torn prefix back off (only while this writer still owns the tail) and fails the append; a torn tail is read-tolerated, a weld is permanent corruption. The append fd opens a+ so the ownership check can read the tail.
…claiming it The tail-match reclaim could truncate a rival's committed record whose final bytes coincide with our torn prefix - committed-data loss, strictly worse than the torn tail it prevented. A short write now just fails the append: the torn tail is the one tolerated shape, skipped on read and truncated by any writer's next repair (verified for both topologies: a resumed single-writer recorder repairs on its first append; every rlm-ledger writer repairs before each append).
This was referenced Sep 7, 2026
This was referenced Sep 7, 2026
fix(tui,coding-agent): survive lone surrogates in table cells and terminate the WebP EXIF scan
#2033
Closed
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5ccfad6. Configure here.
sethkarten
self-requested a review
September 7, 2026 01:55
sethkarten
approved these changes
Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Validation
npm run checkpassed, including lint, TypeScript, installer checks, and browser smoke checks.Refs RES-1260.
Note
High Risk
Touches authentication storage, session transcript repair, daemon protocol/worker recovery, and billing-related provider fields—failures could corrupt sessions, block attach, or mis-price usage.
Overview
This PR hardens durable state and daemon/session I/O while fixing a few provider and runtime edge cases.
Persistence and transcripts: Durable JSON/JSONL writes go through shared atomic rename (
writeFileAtomicSync), includingauth.json(exclusive create + atomic update), settings, cron jobs, telemetry, and session rewrites. Session files get crash repair at open (torn tails, zero-filled lines) when the owning process persists; export and daemon-client read paths use in-memory managers so they do not repair another process’s file. The shared event-log tail rule is unified (unterminated final line skipped on read, truncated on append). Semantic-edge ledgers now append via that substrate.Daemon performance and correctness: Session-list metadata uses incremental resumable scans with a bounded usage cache and serialized per-path reads. Passive subagent topology is memoized until ledger/roster/session inputs change. Snapshot transfer ids bind to the materialized event cursor so a mismatched chunked transfer fails locally (clients resync). Failed workers with a live process can be retried for recovery; known-but-not-ready sessions return
session_recoveringinstead of “unknown session”. Zombie-aware liveness is shared fordaemon ps, leases, supervisor ownership, and update-restart. Idle status sweeps coalesce journal writes and backoff repeated failed summary generations.Session runtime: RLM child usage flushes one attribution batch per settle boundary (not per child message); live “own usage” subtracts not-yet-indexed child spend. Auto-retry ends cleanly when a scheduled
continue()never starts. Compaction keeps only the final turn when the token budget is crossed inside trailing tool results. Bash/output spill paths degrade instead of crashing when temp files fail.Providers (
packages/ai): OpenAI Responses omitservice_tierfor GitHub Copilot (field rejected) but still send explicit tiers elsewhere. Anthropic streaming reprices cache writes frommessage_deltacache_creationbreakdown when usage shifts aftermessage_start.Smaller fixes: Dead IPython kernels drop the memo and restart on next use; piped CLI input joins file/instruction with a blank line; zai default glm-5.3; WebP EXIF scan termination; tail truncation and frontmatter BOM handling; daemon protocol revision 27.
Reviewed by Cursor Bugbot for commit 1c2c9f2. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Harden session persistence with atomic writes, crash repair, and worker recovery
writeFileAtomicSyncin atomic-file.ts and migrates session, auth, event-log, daemon, and harness persistence from bespoke temp-file/rename sequences to the shared utility, preserving symlinks and file modestailLooksDamaged) and repair (repairJsonlDamage) in session-manager.ts that removes zero-filled or torn JSONL tails at open before subsequent appendsreadSessionInfo/scanSessionInfo, so repeated reads resume from the last complete line and concurrent reads for the same path execute in orderreadSessionInfonow serializes same-path reads through a per-path queue and requires a session header as the first parsed entry; files whose first entry is not a header will returnnull.EventLog.replaySyncnow skips any unterminated final line including one that parses as valid JSON, so previously-tolerated partial tails are dropped on replayChanges since #2028 opened
Macroscope summarized 5ccfad6.